﻿using Sgml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace NoASP
{
    public class xmlDoc
    {
        XmlDocument _htmlDocument = new XmlDocument();
        XmlNode _activeChildNode = null;
        Dictionary<string, XmlNode> _elementMap = new Dictionary<string, XmlNode>();

        public static XmlElement ToXmlElement(XmlDocument doc, XElement xElement)
        {
            if (xElement == null) return null;

            XmlElement xmlElement = null;
            XmlReader xmlReader = null;
            try
            {
                xmlReader = xElement.CreateReader();
                xmlElement = doc.ReadNode(xElement.CreateReader()) as XmlElement;
            }
            catch
            {
            }
            finally
            {
                if (xmlReader != null) xmlReader.Close();
            }

            return xmlElement;
        }

        public static string SgmlTranslate(string input)
        {
            var reader = new SgmlReader();
            reader.DocType = "HTML";
            reader.WhitespaceHandling = WhitespaceHandling.None;
            reader.CaseFolding = Sgml.CaseFolding.ToLower;
            reader.InputStream = new StringReader(input);

            var output = new StringWriter();
            var writer = new XmlTextWriter(output);
            writer.Formatting = Formatting.Indented;
            while (reader.Read())
            {
                if (reader.NodeType != XmlNodeType.Whitespace
                   && reader.NodeType != XmlNodeType.Comment)
                    writer.WriteNode(reader, true);
            }
            writer.Close();
            return output.ToString();
        }

        public void LoadXmlContent(string fileContent)
        {
            _elementMap.Clear();
            fileContent = SgmlTranslate(fileContent);

            _htmlDocument.LoadXml(fileContent);
            LearnElement(_htmlDocument.DocumentElement);
        }

        public XmlNode GetElementById(string id)
        {
            if (_elementMap.ContainsKey(id))
            {
                return _elementMap[id];
            }

            return null;
        }

        void LearnElement(XmlNode node)
        {
            if (node.Attributes != null)
            {
                for (int j = 0; j < node.Attributes.Count; j++)
                {
                    if (node.Attributes[j].Name == "id")
                    {
                        string value = node.Attributes[j].Value;
                        if (_elementMap.ContainsKey(value) == false)
                        {
                            _elementMap.Add(value, node);
                        }
                    }
                }
            }

            if (node.ChildNodes != null)
            {
                for (int i = 0; i < node.ChildNodes.Count; i++)
                {
                    XmlNode childNode = node.ChildNodes[i];
                    LearnElement(childNode);
                }
            }
        }

        XmlNode CreateNode(string xml)
        {
            XElement xElement = XElement.Parse(xml);
            XmlElement xmlElement = ToXmlElement(_htmlDocument, xElement);

            LearnElement(xmlElement);

            return xmlElement;
        }

        public void Begin(string element, string text)
        {
            if (_activeChildNode == null)
            {
                _activeChildNode = _htmlDocument.DocumentElement;
            }

            XmlNode node = _htmlDocument.CreateNode(XmlNodeType.Element, element, null);
            _activeChildNode = _activeChildNode.AppendChild(node);
            if (text != string.Empty)
            {
                _activeChildNode.InnerText = text;
            }
        }

        public void Begin(string xml)
        {
            if (_activeChildNode == null)
            {
                _activeChildNode = _htmlDocument.DocumentElement;
            }

            xml = SgmlTranslate(xml);

            XmlNode xmlNode = CreateNode(xml);
            _activeChildNode = _activeChildNode.AppendChild(xmlNode);
        }

        public void AppendAttribute(string name, string value)
        {
            XmlAttribute nameAttribute = _activeChildNode.Attributes[name];
            if (nameAttribute == null)
            {
                nameAttribute = _htmlDocument.CreateAttribute(name);
                nameAttribute.InnerText = value;
                _activeChildNode.Attributes.Append(nameAttribute);
            }
            else
            {
                nameAttribute.InnerText += value;
            }

            if (name == "id")
            {
                if (_elementMap.ContainsKey(value) == false)
                {
                    _elementMap.Add(value, _activeChildNode);
                }
            }
        }

        public void ReplaceAttribute(string name, string value)
        {
            XmlAttribute nameAttribute = _activeChildNode.Attributes[name];
            if (nameAttribute == null)
            {
                nameAttribute = _htmlDocument.CreateAttribute(name);
                nameAttribute.InnerText = value;
                _activeChildNode.Attributes.Append(nameAttribute);
            }
            else
            {
                nameAttribute.InnerText = value;
            }
        }

        public void End()
        {
            _activeChildNode = _activeChildNode.ParentNode;
        }

        public bool Goto(string id)
        {
            _activeChildNode = GetElementById(id);
            if (_activeChildNode != null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void AppendText(string text)
        {
            if (_activeChildNode != null)
            {
                _activeChildNode.InnerText += text;
            }
        }

        public void ReplaceText(string text)
        {
            if (_activeChildNode != null)
            {
                _activeChildNode.InnerText = text;
            }
        }

        public string GetString()
        {
            MemoryStream mem = new MemoryStream();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.OmitXmlDeclaration = true;
            settings.Indent = true;

            XmlWriter writer = XmlWriter.Create(mem, settings);
            _htmlDocument.Save(writer);
            StreamReader sr = new StreamReader(mem, System.Text.Encoding.UTF8);
            mem.Position = 0;
            string xmlString = sr.ReadToEnd();
            sr.Close();
            mem.Close();

            /////////////////////////////////////////
            writer = null;
            settings = null;
            mem = null;

            xmlString = xmlString.Replace("&lt;", "<");
            xmlString = xmlString.Replace("&gt;", ">");
            xmlString = xmlString.Replace("&amp;", "&");
            return xmlString;
        }
    }
}
